package Mqtt

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"crypto/tls"
	"fmt"
	"gitee.com/xuyiping_admin/pkg/di"
	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
	"go.uber.org/zap"
	"kpt-dtm/config"
	"time"

	Mqtt "github.com/eclipse/paho.mqtt.golang"
)

var Module = di.Options(di.Provide(NewMqttManager))

// ClientManager 客户端管理器接口
type ClientManager interface {
	GetClient(index string) (Client, error)
	GetAllClients() map[string]Client
	Publish(index string, payload interface{}) Mqtt.Token
	Subscribe(index string, callback Mqtt.MessageHandler) Mqtt.Token
}

// MqttManager 客户端管理器实现
type MqttManager struct {
	Clients map[string]*ALiYunMqttEntry
}

func NewMqttManager(cfg *config.AppConfig) *MqttManager {
	manager := &MqttManager{
		Clients: make(map[string]*ALiYunMqttEntry),
	}
	// 例如：遍历 cfg.DTMConfig 或其他配置来创建多个客户端
	for pastureId, dtmCfg := range cfg.DTMConfig {
		client := NewSingleALiYunMqtt(dtmCfg)
		manager.Clients[pastureId] = client
	}
	return manager
}

type Client interface {
	Publish(payload interface{}) Mqtt.Token
	Subscribe(callback Mqtt.MessageHandler) Mqtt.Token
}

func NewSingleALiYunMqtt(dtmCfg *config.DTMConfig) *ALiYunMqttEntry {
	var raw_broker bytes.Buffer
	raw_broker.WriteString("tls://")
	raw_broker.WriteString(fmt.Sprintf("%s:1883", dtmCfg.YYNConfig.Mqtt.Host))
	opts := Mqtt.NewClientOptions().AddBroker(raw_broker.String())

	auth := CalculateSign(dtmCfg)
	opts.SetAutoReconnect(true)
	opts.SetClientID(auth.MqttClientId)
	opts.SetUsername(auth.Username)
	opts.SetPassword(auth.Password)
	opts.SetKeepAlive(60 * 2 * time.Second)
	opts.SetDefaultPublishHandler(handleBack)
	opts.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})

	client := Mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		zaplog.Error("Mqtt connection failed",
			zap.Any("config", dtmCfg),
			zap.Error(token.Error()))
		panic(token.Error())
	}
	res := &ALiYunMqttEntry{
		Client: client,
		DtmCfg: dtmCfg,
	}
	return res
}

type ALiYunMqttEntry struct {
	Mqtt.Client
	DtmCfg *config.DTMConfig
}

type AuthInfo struct {
	Password, Username, MqttClientId string
}

func (m MqttManager) GetClient(index string) (Client, error) {
	if client, exists := m.Clients[index]; exists {
		return client, nil
	}
	return nil, fmt.Errorf("client with index %d not found", index)
}

func (m MqttManager) GetAllClients() map[string]Client {
	result := make(map[string]Client)
	for index, client := range m.Clients {
		result[index] = client
	}
	return result
}

// Publish ClientManager 接口的方法
func (m MqttManager) Publish(index string, payload interface{}) Mqtt.Token {
	client, err := m.GetClient(index)
	if err != nil {
		return &errorToken{err: err}
	}
	zaplog.Info("Publish",
		zap.Any("client", client),
		zap.Any("payload", payload),
		zap.Any("index", index))
	return client.Publish(payload)
}

func (m MqttManager) Subscribe(index string, callback Mqtt.MessageHandler) Mqtt.Token {
	client, err := m.GetClient(index)
	if err != nil {
		return &errorToken{err: err}
	}
	return client.Subscribe(callback)
}

func (A *ALiYunMqttEntry) Publish(payload interface{}) Mqtt.Token {
	zaplog.Info("Publish", zap.Any("payload", payload))
	return A.Client.Publish(A.DtmCfg.YYNConfig.Mqtt.TopicName, byte(A.DtmCfg.YYNConfig.Mqtt.Qos), A.DtmCfg.YYNConfig.Mqtt.Retained, payload)
}

func (A *ALiYunMqttEntry) Subscribe(callback Mqtt.MessageHandler) Mqtt.Token {
	return A.Client.Subscribe(A.DtmCfg.YYNConfig.Mqtt.TopicName, byte(A.DtmCfg.YYNConfig.Mqtt.Qos), callback)
}

func CalculateSign(cfg *config.DTMConfig) *AuthInfo {
	timeStamp := time.Now().Unix()
	var raw_passwd bytes.Buffer
	raw_passwd.WriteString("clientId" + cfg.YYNConfig.Mqtt.ProductKey)
	raw_passwd.WriteString("deviceName")
	raw_passwd.WriteString(cfg.YYNConfig.Mqtt.DeviceName)
	raw_passwd.WriteString("productKey")
	raw_passwd.WriteString(cfg.YYNConfig.Mqtt.ProductKey)
	raw_passwd.WriteString("timestamp")
	raw_passwd.WriteString(fmt.Sprintf("%d", timeStamp))
	// hmac, use sha1
	mac := hmac.New(sha1.New, []byte(cfg.YYNConfig.Mqtt.DeviceSecret))
	mac.Write([]byte(raw_passwd.String()))
	password := fmt.Sprintf("%02x", mac.Sum(nil))
	username := cfg.YYNConfig.Mqtt.DeviceName + "&" + cfg.YYNConfig.Mqtt.ProductKey
	var MQTTClientId bytes.Buffer
	MQTTClientId.WriteString(cfg.YYNConfig.Mqtt.ProductKey)
	// hmac, use sha1; securemode=2 means TLS connection
	MQTTClientId.WriteString("|securemode=2,_v=paho-go-1.0.0,signmethod=hmacsha1,timestamp=")
	MQTTClientId.WriteString(fmt.Sprintf("%d", timeStamp))
	MQTTClientId.WriteString("|")
	return &AuthInfo{Password: password, Username: username, MqttClientId: MQTTClientId.String()}
}

var handleBack Mqtt.MessageHandler = func(client Mqtt.Client, msg Mqtt.Message) {
	zaplog.Info("SetDefaultPublishHandler", zap.Any("topic", msg.Topic()), zap.Any("message", string(msg.Payload())))
}

// 错误 token 实现
type errorToken struct {
	err error
}

func (e *errorToken) Wait() bool                     { return false }
func (e *errorToken) WaitTimeout(time.Duration) bool { return false }
func (e *errorToken) Error() error                   { return e.err }
func (e *errorToken) Done() <-chan struct{} {
	ch := make(chan struct{})
	close(ch)
	return ch
}
